﻿using Masuit.Tools.Models;
using Masuit.Tools.Systems;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;
using YaseiAkira.AJ.Util;
using WebService.Models;
using System.Text;
using WebService.Form;
using Microsoft.AspNetCore.Http;
using Yitter.IdGenerator;
using System.Web;

namespace WebService.Upload
{
    public static class Uploader
    {
        public class MediaItem
        {
            public long Id { get; set; }
            public string Category { get; set; }
            public long EndId { get; set; }
            public string Name { get; set; }
            public string Ext { get; set; }
            public string Url { get; set; }
            public DateTime CreateDate { get; set; }
            public long Uploader { get; set; }
            public bool SecretFile { get; set; }
        }

        private static readonly string _root = "/uploads";
        public static readonly string SECRETDIR = "/secret/cert";

        /// <summary>
        /// 这个是每一次从Http请求的section中读出文件数据的大小，单位是Byte即字节，这里设置为1024的意思是，每次从Http请求的section数据流中读取出1024字节的数据到服务器内存中，然后写入下面targetFileStream的文件流中，可以根据服务器的内存大小调整这个值。这样就避免了一次加载所有上传文件的数据到服务器内存中，导致服务器崩溃。
        /// </summary>
        const int FILE_WRITE_SIZE = 84975;

        public static readonly string[] ALLOWEXTS = new string[]
        {
            ".jpg",".jpeg",".png",".ico",".bmp",".gif",".tif",".tga",".mp3",".wma",".ape",".flac",".aac",".amr",".m4a",".m4r",".ogg",".wav",".flv",".3gp",".rmvb",".rm",".swf",".mp4",".mkv",".avi",".mov",".wmv",".p12",".pem",".zip",".rar",".7z",".pdf",".xls",".xlsx",".doc",".docx",".ppt",".pptx",".cer"
        };

        private static readonly FormOptions _defaultFormOptions = new FormOptions();



        public static PagedList<FileInfo> GetUploadedListByFolder(string name, int page = 1, int limit = 10)
        {
            var result = new List<FileInfo>();
            var dirs = new List<DirectoryInfo>();

            if (string.IsNullOrWhiteSpace(name))
            {
                dirs = new DirectoryInfo(Util.MapPath(_root)).GetDirectories().ToList();
            }
            else
            {
                var local = Util.MapPath($"{_root}/{name}", false);

                if (Directory.Exists(local))
                {
                    dirs.Add(new DirectoryInfo(local));
                }
            }

            if (dirs.Count == 0)
            {
                return new PagedList<FileInfo>(new List<FileInfo>(), page, limit, 0);
            }

            foreach (var dir in dirs)
            {
                result.AddRange(dir.GetFiles());
            }

            return result.OrderByDescending(p => p.CreationTime).ToPagedList(page, limit);
        }

        public static string[] GetUploadFolders()
        {
            var dirInfo = new DirectoryInfo(Util.MapPath(_root));

            return dirInfo.GetDirectories().Select(p => p.Name).ToArray();
        }

        public static bool RenameUploadFolder(string from, string to)
        {
            var fromLocalFile = string.Empty;
            if (FormChecker.WINDOWILLEGALFILENAME.IsMatch(from) || from.Length > 8)
            {
                return false;
            }

            var toLocalFile = string.Empty;
            if (FormChecker.WINDOWILLEGALFILENAME.IsMatch(to) || to.Length > 8)
            {
                return false;
            }

            fromLocalFile = Util.MapPath($"{_root}/{from}", false);

            if (!Directory.Exists(fromLocalFile))
            {
                return false;
            }

            toLocalFile = Util.MapPath($"{_root}/{to}");

            try
            {
                var dirInfo = new DirectoryInfo(fromLocalFile);

                dirInfo.MoveTo(toLocalFile);

                return true;
            }
            catch
            {
                return false;
            }
        }

        public static bool DeleteUploadFolder(string name)
        {
            var localFile = string.Empty;
            if (FormChecker.WINDOWILLEGALFILENAME.IsMatch(name) || name.Length > 8)
            {
                return false;
            }

            localFile = Util.MapPath($"{_root}/{name}", false);

            if (!Directory.Exists(localFile))
            {
                return false;
            }

            try
            {
                var dirInfo = new DirectoryInfo(localFile);
                dirInfo.Delete(true);

                return true;
            }
            catch
            {
                return false;
            }
        }

        public static bool CreateUploadFolder(string name, out string localFile)
        {
            localFile = string.Empty;
            if (FormChecker.WINDOWILLEGALFILENAME.IsMatch(name) || name.Length > 8)
            {
                return false;
            }

            localFile = Util.MapPath($"{_root}/{name}");

            return true;
        }

        public static async Task<ProcessResult> UploadFilesAsync(this HttpRequest request,
            string category)
        {
            var result = new ProcessResult();

            if (!MultipartRequestHelper.IsMultipartContentType(request.ContentType))
            {
                result.SetError($"Expected a multipart request, but got {request.ContentType}");
                return result;
            }

            if (!CreateUploadFolder(category, out var localDir))
            {
                result.SetError($"参数 {nameof(category)} 存在非法字符或超长(8)");
                return result;
            }

            var virtualDir = $"{_root}/{category}";

            var medias = new List<MediaItem>();

            var boundary = MultipartRequestHelper.GetBoundary(
                MediaTypeHeaderValue.Parse(request.ContentType),
                _defaultFormOptions.MultipartBoundaryLengthLimit);
            var reader = new MultipartReader(boundary, request.Body);

            var section = await reader.ReadNextSectionAsync();//用于读取Http请求中的第一个section数据
            while (section != null)
            {
                ContentDispositionHeaderValue contentDisposition;
                var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition);

                if (hasContentDispositionHeader
                    && MultipartRequestHelper.HasFileContentDisposition(contentDisposition))
                {
                    /*
                    用于处理上传文件类型的的section
                    -----------------------------99614912995
                    Content - Disposition: form - data; name = "files"; filename = "Misc 002.jpg"

                    ASAADSDSDJXCKDSDSDSHAUSAUASAASSDSDFDSFJHSIHFSDUIASUI+/==
                    -----------------------------99614912995
                    */

                    var fileName = HttpUtility.UrlDecode(MultipartRequestHelper.GetFileName(contentDisposition));

                    // 判断合法文件
                    var ext = Path.GetExtension(fileName).ToLower();

                    var secret = new string[] { ".p12", ".pem" }.Contains(ext);

                    if (secret)
                    {
                        virtualDir = SECRETDIR;
                        localDir = Util.MapPath(virtualDir);
                        if (!Directory.Exists(localDir))
                        {
                            Directory.CreateDirectory(localDir);
                        }
                    }

                    // 合法扩展名称检测
                    if (!ALLOWEXTS.Contains(ext))
                    {
                        result.SetError("错误的文件格式");
                        return result;
                    }

                    // 获取原始文件名,判断是否已经上传过
                    var localFile = Path.Combine(localDir, fileName);

                    if (!File.Exists(localFile))
                    {
                        if (FormChecker.IMAGEREGEX.IsMatch(ext))
                        {
                            var saver = new ImageSaver();
                            await saver.CompressSave(section.Body, localDir, fileName);
                        }
                        else
                        {
                            using var targetFileStream = File.Create(localFile);
                            await section.Body.CopyToAsync(targetFileStream, FILE_WRITE_SIZE);
                            //section.Body是System.IO.Stream类型，表示的是Http请求中一个section的数据流，从该数据流中可以读出每一个section的全部数据，所以我们下面也可以不用section.Body.CopyToAsync方法，而是在一个循环中用section.Body.Read方法自己读出数据，再将数据写入到targetFileStream
                        }
                    }

                    var media = new MediaItem
                    {
                        Id = SnowFlake.GetInstance().GetLongId(),
                        Category = category,
                        Name = fileName,
                        Ext = ext,
                        Url = secret ? fileName : $"{request.GetDomain()}{virtualDir}/{fileName}",
                        CreateDate = DateTime.Now,
                        //Uploader = curLoginUser.Id,
                        SecretFile = secret,
                    };

                    medias.Add(media);
                    /*
                    用于处理表单键值数据的section
                    -----------------------------99614912995
                    Content - Disposition: form - data; name = "SOMENAME"

                    Formulaire de Quota
                    -----------------------------99614912995
                    */

                }
                else if (MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition))
                {
                    //// Content-Disposition: form-data; name="key"
                    ////
                    //// value

                    //// Do not limit the key name length here because the 
                    //// multipart headers length limit is already in effect.
                    //var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name);
                    //var encoding = GetEncoding(section);
                    //using (var streamReader = new StreamReader(
                    //    section.Body,
                    //    encoding,
                    //    detectEncodingFromByteOrderMarks: true,
                    //    bufferSize: 1024,
                    //    leaveOpen: true))
                    //{
                    //    // The value length limit is enforced by MultipartBodyLengthLimit
                    //    var value = await streamReader.ReadToEndAsync();
                    //    if (String.Equals(value, "undefined", StringComparison.OrdinalIgnoreCase))
                    //    {
                    //        value = String.Empty;
                    //    }
                    //    formAccumulator.Append(key.Value, value); // For .NET Core <2.0 remove ".Value" from key

                    //    if (formAccumulator.ValueCount > _defaultFormOptions.ValueCountLimit)
                    //    {
                    //        throw new InvalidDataException($"Form key count limit {_defaultFormOptions.ValueCountLimit} exceeded.");
                    //    }
                    //}
                }

                // Drains any remaining section body that has not been consumed and
                // reads the headers for the next section.
                section = await reader.ReadNextSectionAsync();//用于读取Http请求中的下一个section数据
            }

            // Bind form data to a model
            result.SetSuccess(medias);

            return result;
        }

        private static Encoding GetEncoding(MultipartSection section)
        {
            MediaTypeHeaderValue mediaType;
            var hasMediaTypeHeader = MediaTypeHeaderValue.TryParse(section.ContentType, out mediaType);
            // UTF-7 is insecure and should not be honored. UTF-8 will succeed in 
            // most cases.
            if (!hasMediaTypeHeader || Encoding.UTF7.Equals(mediaType.Encoding))
            {
                return Encoding.UTF8;
            }
            return mediaType.Encoding;
        }


    }
}
